package main

import (
	"errors"
	"fmt"
	"syscall"
	"unsafe"
)

var GetErrorProc *syscall.Proc
var TestOnlineProc *syscall.Proc
var CreateProgramProc *syscall.Proc
var InitLedProc *syscall.Proc
var InitLedRgbProc *syscall.Proc
var PowerOnOffProc *syscall.Proc
var NetWorkSendProc *syscall.Proc
var AddDigitalClockAreaProc *syscall.Proc
var LedInitServerProc *syscall.Proc
var AddProgramProc *syscall.Proc
var AddImageTextAreaProc *syscall.Proc
var AddStaticTextToImageTextAreaProc *syscall.Proc
var AddNeiMaAreaProc *syscall.Proc
var RefreshNeiMaAreaProc *syscall.Proc

func init() {
	path := "lv_led_64.dll"
	//path := "lv_led_MBCS_64.dll"
	//path := "./lv_led.dll"
	dll := syscall.MustLoadDLL(path)
	GetErrorProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "GetError"))
	TestOnlineProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "TestOnline"))
	CreateProgramProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "CreateProgramEx"))
	PowerOnOffProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "PowerOnOff"))
	InitLedProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "InitLed"))
	NetWorkSendProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "Send"))
	AddDigitalClockAreaProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "AddDigitalClockArea"))
	LedInitServerProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "LedInitServer"))
	AddProgramProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "AddProgram"))
	AddImageTextAreaProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "AddImageTextArea"))
	AddStaticTextToImageTextAreaProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "AddStaticTextToImageTextArea"))
	AddNeiMaAreaProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "AddNeiMaArea"))
	RefreshNeiMaAreaProc = dll.MustFindProc(fmt.Sprintf("LV_%s", "RefreshNeiMaArea"))
}

func Run(proc *syscall.Proc, args ...uintptr) (ret uintptr, err error) {
	ret, _, e := syscall.SyscallN(
		proc.Addr(), args...)
	if e.Error() != "The operation completed successfully." {
		return ret, errors.New(e.Error())
	}
	return ret, nil
}

// HPROGRAM LV_CreateProgramEx(int LedWidth,int LedHeight,int ColorType,int GrayLevel,int SaveType)
func CreateProgram(width, height, colorType, grayLevel, saveType int) (uintptr, error) {
	/****
	LedWidth 屏的宽度
	LedHeight 屏的高度
	ColorType 屏的颜色 1.单色 2.双基色 3. 三基色 注：C 卡全彩参数为 3 X 系列卡参数固定为 4
	GrayLevel 灰度等级 赋值 1-5 对应的灰度等级分别为 无,4,8,16,32 注：目前 C 系列的卡才支持，其它型号
	（T,A,U,XC,W,E,X）参数必须为 0
	SaveType 节目保存位置，默认为 0 保存为 flash 节目，3 保存为 ram 节目。注：flash 节目掉电不清除，ram 节目掉电清除。应用
	场景需要实时刷新的，建议保存为 ram 节目目前仅C卡程序才支持切换, 其他卡默认出货为flash程序,如果需要RAM程序请联系
	业务或者在官网下载,然后使用Led Player对卡进行升级
	****/
	ret, err := Run(CreateProgramProc, uintptr(width), uintptr(height), uintptr(colorType), uintptr(grayLevel), uintptr(saveType))
	return ret, err
}

// int LV_AddProgram(HPROGRAM hProgram,int ProgramNo,int ProgramTime,int LoopCount)
func AddProgram(hProgram uintptr, ProgramNo, ProgramTime, LoopCount int) (uintptr, error) {
	/*
		hProgram 节目对象句柄
		返回值：
		ProgramNo
		ProgramTime
		LoopCount
		节目号 从 0 开始(0-255)
		节目播放时长 0.节目播放时长 非 0.指定播放时长
		循环播放次数 1-255*/
	return Run(AddProgramProc, hProgram, uintptr(ProgramNo), uintptr(ProgramTime), uintptr(LoopCount))
}

// LV_AddImageTextArea(HPROGRAM hProgram,int ProgramNo,int AreaNo,LPAREARECT pAreaRect, int nLayout)
func AddImageTextArea(hProgram uintptr, ProgramNo, AreaNo int, pAreaRect *AREARECT, nLayout int) (uintptr, error) {
	/*hProgram 节目对象句柄
	ProgramNo 节目号 从 0 开始(0-255)
	AreaNo 区域号 (1-255)
	pAreaRect 区域坐标属性，设置方式见 AREARECT 结构体注示
	nLayout 区域层号，1.前景区（默认） 0.背景区 注：除C系列，其它默认为1*/
	return Run(AddImageTextAreaProc, hProgram, uintptr(ProgramNo), uintptr(AreaNo),
		uintptr(unsafe.Pointer(pAreaRect)), uintptr(nLayout))
}

// int LV_AddStaticTextToImageTextArea(
// HPROGRAM hProgram,int ProgramNo,int AreaNo,int AddType,LPCTSTR AddStr,
// FONTPROP *pFontProp,int DelayTime,int nAlignment,BOOL IsVCenter)
func AddStaticTextToImageTextArea(
	hProgram uintptr, ProgramNo int32, AreaNo int32, AddType int32, AddStr string,
	pFontProp *FONTPROP,
	DelayTime int32, nAlignment int32, IsVCenter int32) (uintptr, error) {
	/*hProgram 节目对象句柄
	ProgramNo 节目号 从 0 开始(0-255)
	AreaNo 区域号 (1-255)
	AddType 添加的类型 0.为字符串 1.文件（DLL 支持 txt rtf SO 库只支持 txt）
	AddStr AddType 为 0 则为字符串数据,AddType 为 1 则为文件路径
	pFontProp 字体的属性，设置方式见FONTPROP 结构体注示
	DelayTime 显示的时长 1~65535
	nAlignment 水平对齐样式，0.左对齐 1.水平居中 2.右对齐 （注意：只对字符串和txt 文件有效）
	IsVCenter 是否垂直居中 0.置顶（默认） 1.垂直居中*/
	return Run(AddStaticTextToImageTextAreaProc, hProgram, uintptr(ProgramNo), uintptr(AreaNo),
		uintptr(AddType), BytePtr(AddStr),
		uintptr(unsafe.Pointer(pFontProp)),
		uintptr(DelayTime), uintptr(nAlignment), uintptr(IsVCenter))
}

// int LV_AddNeiMaArea (HPROGRAM hProgram,int ProgramNo,int AreaNo, LPAREARECT pAreaRect , PLAYPROP *pPlayProp)
func AddNeiMaArea(hProgram uintptr, ProgramNo, AreaNo int32, NeiMaStr string,
	FontSize int32, FontColor int32,
	pAreaRect *AREARECT, pPlayProp *PLAYPROP) (uintptr, error) {
	/*
		hProgram 节目对象句柄
		ProgramNo 节目号 从 0 开始(0-255)
		AreaNo 区域号 (1-255)
		pAreaRect 区域坐标属性，设置方式见AREARECT结构体注示
		NeiMaStr 文本字符串 注：字符串编码是 GB2312
		FontSize 字体大小 16 24 32
		FontColor 文字颜色 格式BBGGRR 0xff 红色 0xff00 绿色 0xffff 黄色
		pPlayProp 显示的属性，设置方式见PLAYPROP 结构体注示
	*/
	char := StringToChar(NeiMaStr)
	return Run(AddNeiMaAreaProc, hProgram,
		uintptr(ProgramNo), uintptr(AreaNo),
		uintptr(unsafe.Pointer(pAreaRect)),
		uintptr(unsafe.Pointer(&char[0])),
		uintptr(FontSize),
		uintptr(FontColor),
		uintptr(unsafe.Pointer(pPlayProp)))
}

func RefreshNeiMaArea(ipAddr, NeiMaStr string) (uintptr, error) {
	return Run(RefreshNeiMaAreaProc,
		NewComInfoUintPtr(ipAddr),
		BytePtr(NeiMaStr),
	)
}

func Send(ipAddr string, hProgram uintptr) (uintptr, error) {
	return Run(NetWorkSendProc, NewComInfoUintPtr(ipAddr), hProgram)
}

// AdjustTime(String IpStr)
// /*********************************************************************************************
// * AdjustTime 校时
// *
// * 参数说明
// * IpStr LED屏的IP
// * 返回值
// * 0 成功
// * 非0 失败
// ********************************************************************************************/
func AdjustTime(ipAddr string) (uintptr, error) {
	return Run(CreateProgramProc, NewComInfoUintPtr(ipAddr))
}

// PowerOnOff(String IpStr,int OnOff)
// /*********************************************************************************************
// * PowerOnOff 开关屏
// *
// * 参数说明
// * IpStr LED屏的IP
// * OnOff 开关值 0.开屏 1.关屏 2.重启
// * 返回值
// * 0 成功
// * 非0 失败
// ********************************************************************************************/

func PowerOnOff(ipAddr string, OnOff int) (uintptr, error) {
	info := NewComInfoUintPtr(ipAddr)
	return Run(PowerOnOffProc, info, uintptr(OnOff))
}

// TimePowerOnOff(String IpStr,int StartHour,int StartMinute,int
// EndHour,int EndMinute)
// /*********************************************************************************************
// * TimePowerOnOff 定时开关屏
// *
// * 参数说明 注：当参数起始时间和结束时间都都为0时，既是取消定时开关屏
// * IpStr LED屏的IP
// * StartHour 起始小时
// * StartMinute 起始分钟
// * EndHour 结束小时
// * EndMinute 结束分钟
// * 返回值
// * 0 成功
// * 非0 失败
// ********************************************************************************************/
func TimePowerOnOff(ipAddr string, StartHour int, StartMinute int, EndHour int, EndMinute int) (uintptr, error) {
	return Run(CreateProgramProc, BytePtr(ipAddr), uintptr(StartHour), uintptr(StartMinute), uintptr(EndHour), uintptr(EndMinute))
}

// LedInitServer(int port)
// /*********************************************************************************************
// * LedInitServer 启动控制卡心跳包服务。注：C2M C4M T4C T4G E5均支持,其他类型请咨询业务人员
// *
// * 参数说明
// * port 监听的端口
// * 返回值
// * 0 成功
// * 非0 失败，调用LV_GetError来获取错误信息
// ********************************************************************************************/
func LedInitServer(port int) (uintptr, error) {
	return Run(LedInitServerProc, uintptr(port))
}

// InitLedType(int LedType)
// /********************************************************************************************
// * InitLedType 初始化控制卡型号，程序启动，初始化调用一次即可
// *
// * 参数说明
// * LedType 卡型号 0 T/A/U/XC/W 系列
// 1 E 系列
// 2 X 系列
// 3 C 系列
// ********************************************************************************************/
func InitLed(LedType, nRgb int) (uintptr, error) {
	return Run(InitLedProc, uintptr(LedType), uintptr(nRgb))
}

// InitLedRgb (int Rgb)
// /********************************************************************************************
// * InitLedRgb 当Led上显示的文字区域的颜色与下发的不一致, 请确认Led 屏的RGB顺序,并调用此接口
// *
// * 参数说明
// * Rgb 模组的RGB顺序,仅C卡有效,其他卡固定为0. C卡时, 0: R->G->B 1: G->R->B 2:R->B->G
// 3:B->R->G 4:B->G->R 5:G->B->R
// ********************************************************************************************/
func InitLedRgb(Rgb int) (uintptr, error) {
	return Run(InitLedRgbProc, uintptr(Rgb))
}

func GetError(r1 uintptr) string {
	bytes := make([]uint16, 200)
	_, _, e := syscall.SyscallN(GetErrorProc.Addr(),
		r1,
		uintptr(200),
		uintptr(unsafe.Pointer(&bytes[0])))
	if e.Error() != "The operation completed successfully." {
		return e.Error()
	}
	text := syscall.UTF16ToString(bytes)
	println(text)
	return text
}
